import { plugin, AMessage } from 'alemonjs'
import { oImages } from '../api'

import { gamemap, ABC, cwd } from '../config/metadata'
import { command } from '../config/command'

export class newcomer extends plugin {
  constructor() {
    super({
      dsc: '扫雷小游戏',
      priority: 5000,
      rule: [...command.minesweeper]
    })
  }
  /**
   * 游戏设置
   */
  async setup(e: AMessage) {
    newgame(e)
    if (gamemap[e.channel_id].game) return e.reply(`游戏已开始，不可设置`)
    if (/^(#|\/)?(扫雷|排雷)设置地?雷数量?\d+/.test(e.msg)) {
      const emsg = e.msg.match(/\d+/)
      if (
        +emsg[0] >
        +gamemap[e.channel_id].rows * +gamemap[e.channel_id].cols
      ) {
        return e.reply('设置超出阈值')
      }
      gamemap[e.channel_id].z = emsg[0] ? emsg[0] : 10

      e.reply('修改雷数：' + gamemap[e.channel_id].z)
    }

    if (/^(#|\/)?(扫雷|排雷)设置布局 ?\d+\s\d+(\s\d+)?/.test(e.msg)) {
      const emsg = e.msg.match(/\d+/g)
      if (+emsg[0] > 100 || +emsg[1] > 26 || +emsg[2] > +emsg[0] * +emsg[1])
        return e.reply('设置超出阈值')
      gamemap[e.channel_id].z = emsg[2] ? emsg[2] : 10
      gamemap[e.channel_id].rows = emsg[0] ? emsg[0] : 9
      gamemap[e.channel_id].cols = emsg[1] ? emsg[1] : 9
      e.reply(
        `修改游戏布局\n宽：${gamemap[e.channel_id].rows}\n高：${gamemap[e.channel_id].cols
        }\n雷：${gamemap[e.channel_id].z}`
      )
    }
    if (/^(#|\/)?(扫雷|排雷)设置布局初始化/.test(e.msg)) {
      gamemap[e.channel_id].z = 10
      gamemap[e.channel_id].rows = 9
      gamemap[e.channel_id].cols = 9
      e.reply(
        `初始化布局\n宽：${gamemap[e.channel_id].rows}\n高：${gamemap[e.channel_id].cols
        }\n雷：${gamemap[e.channel_id].z}`
      )
    }
  }

  /**
   * 开始游戏
   */
  async goo(e: AMessage) {
    newgame(e)
    gamemap[e.channel_id].command = e.msg
    goo(e)
  }

  /**
   * 设置难度
   */
  async goolevel(e: AMessage) {
    newgame(e)
    if (gamemap[e.channel_id].game) {
      e.reply('排雷游戏已经开始')
      generate(e)
      return
    }
    const emsg = e.msg.replace(/^(#|\/)?(扫雷|排雷)难度(:|：)?/, '')
    switch (emsg) {
      case '简单':
        gamemap[e.channel_id].z = 10
        gamemap[e.channel_id].rows = 10
        gamemap[e.channel_id].rows = 9
        gamemap[e.channel_id].cols = 9
        break
      case '困难':
        gamemap[e.channel_id].z = 60
        gamemap[e.channel_id].rows = 26
        gamemap[e.channel_id].cols = 26
        break
      case '中等':
        gamemap[e.channel_id].z = 40
        gamemap[e.channel_id].rows = 16
        gamemap[e.channel_id].cols = 16
        break
      case '普通':
        gamemap[e.channel_id].z = 40
        gamemap[e.channel_id].rows = 16
        gamemap[e.channel_id].cols = 16
        break
      default:
        break
    }
    gamemap[e.channel_id].command = e.msg
    goo(e)
  }

  /**
   * 结束游戏
   */
  async ov(e: AMessage) {
    newgame(e)
    if (gamemap[e.channel_id].game) {
      if (gamemap[e.channel_id].player == e.user_id || e.isMaster) {
        e.reply('已结排雷游戏')
        over(e)
      } else {
        e.reply('你不是游戏发起人，不能主动结束游戏')
      }
    } else {
      e.reply('游戏没有开始呢~')
    }
  }

  /**
   * 取消标记
   */
  async removemark(e: AMessage) {
    newgame(e)
    if (!gamemap[e.channel_id].game) {
      e.reply('本群游戏未开始，请先发送“/排雷“')
      return false
    }
    gamemap[e.channel_id].command = e.msg

    if (!e.msg.match(/([a-zA-Z])(\d+)/g)) {
      e.reply('指令不规范~请参考“/排雷帮助”')
      return false
    }

    e.msg.match(/([a-zA-Z])(\d+)/g).forEach((v: string) => {
      // 获取坐标
      // emsg[1]为字母，emsg[2]为数字
      const emsg = v.match(/([a-zA-Z])(\d+)/)
      const letterA = emsg[1].toUpperCase() // 大写字母
      const letter = letterA.charCodeAt(0) - 65 // 转数字 A = 0

      const axis = `${letter}-${emsg[2]}`
      // console.log('获取坐标' + axis);

      // 判断坐标是否存在
      if (gamemap[e.channel_id].archive[axis]) {
        // 判断坐标是否显示
        if (!gamemap[e.channel_id].isopen[axis]) {
          gamemap[e.channel_id].mark[axis] = false
        }
      }
    })
    generate(e)
  }

  /**
   * 标记
   */
  async mark(e: AMessage) {
    newgame(e)
    if (!gamemap[e.channel_id].game) {
      e.reply('本群游戏未开始，请先发送“/排雷“')
      return false
    }
    gamemap[e.channel_id].command = e.msg

    if (!e.msg.match(/([a-zA-Z])(\d+)/g)) {
      e.reply('指令不规范~请参考“/排雷帮助”')
      return false
    }
    gamemap[e.channel_id].command = e.msg

    e.msg.match(/([a-zA-Z])(\d+)/g).forEach((v: string) => {
      // 获取坐标
      // emsg[1]为字母，emsg[2]为数字
      const emsg = v.match(/([a-zA-Z])(\d+)/)
      const letterA = emsg[1].toUpperCase() // 大写字母
      const letter = letterA.charCodeAt(0) - 65 // 转数字 A = 0

      const axis = `${letter}-${emsg[2]}`

      // 判断坐标是否存在
      if (gamemap[e.channel_id].archive[axis]) {
        // 判断坐标是否显示
        if (!gamemap[e.channel_id].isopen[axis]) {
          gamemap[e.channel_id].mark[axis] = true
        }
      }
    })
    generate(e)
  }

  /**
   * 游戏指令
   */
  async gooplye(e: AMessage) {
    newgame(e)
    if (!gamemap[e.channel_id].game) {
      e.reply('本群游戏未开始，请先发送“/排雷“')
      return false
    }
    gamemap[e.channel_id].command = e.msg

    if (!e.msg.match(/([a-zA-Z])(\d+)/g)) {
      e.reply('指令不规范~请参考“/排雷帮助”')
      return false
    }
    gamemap[e.channel_id].command = e.msg

    /**
     * 玩家点击 A1 获得坐标 01
     * 判断 ‘标记雷’ 点击的是否为雷 -> 结束游戏
     * 判断 ‘标记翻开’ 得到剩余数量与雷数量是否一致，一致则胜利 -> 结束游戏
     * 判断 ‘添加数字后的地图数据’ 是否为‘-’，扩散显示，遇到数字停下
     */

    let gameover = false
    let outcome = ''
    let kk = 0

    // 获取坐标
    // emsg[1]为字母，emsg[2]为数字
    e.msg.match(/([a-zA-Z])(\d+)/g).forEach((v: string) => {
      const emsg = v.match(/([a-zA-Z])(\d+)/)
      const letterA = emsg[1].toUpperCase() // 大写字母
      const letter = letterA.charCodeAt(0) - 65 // 转数字 A = 0

      const axis = `${letter}-${emsg[2]}`

      // 判断坐标是否存在
      if (gamemap[e.channel_id].archive[axis]) {
        // 判断雷
        if (gamemap[e.channel_id].mines[axis]) {
          gamemap[e.channel_id].archive[axis] = 'x'
          gamemap[e.channel_id].isopen[axis] = true
          gameover = true
          outcome = 'fail'
          return
        }

        // 扩散显示
        // 当前位置
        const indexs = axis.split('-')

        if (gamemap[e.channel_id].archive[axis] === '0') {
          try {
            const loop = (indexs: (string | number)[]) => {
              ;[
                [+indexs[0] - 1, +indexs[1] - 1],
                [+indexs[0] - 1, +indexs[1]],
                [+indexs[0] - 1, +indexs[1] + 1],
                [+indexs[0], +indexs[1] - 1],
                [+indexs[0], +indexs[1] + 1],
                [+indexs[0] + 1, +indexs[1] - 1],
                [+indexs[0] + 1, +indexs[1]],
                [+indexs[0] + 1, +indexs[1] + 1]
              ].forEach(subIndexs => {
                // console.log('subIndexs:',subIndexs);
                const el = gamemap[e.channel_id].archive[subIndexs.join('-')]
                // console.log('el:',el);
                if (el) {
                  const thisel = gamemap[e.channel_id].mark[subIndexs.join('-')]

                  // 值为’-‘开始扩散
                  if (
                    el === '0' &&
                    gamemap[e.channel_id].isopen[subIndexs.join('-')] === false
                  ) {
                    if (thisel !== true) {
                      // 没被标记显示
                      gamemap[e.channel_id].isopen[subIndexs.join('-')] = true
                      ++kk
                      loop(subIndexs)
                    }
                  } else {
                    if (thisel !== true) {
                      // 没被标记显示
                      gamemap[e.channel_id].isopen[subIndexs.join('-')] = true
                    }
                  }
                }
              })
            }
            loop(indexs)
          } catch (error) {
            e.reply('调用栈溢出，请缩小布局大小')
            return
          }
        }
        // 显示指定坐标块
        gamemap[e.channel_id].isopen[axis] = true
      }
    })
    if (outcome == 'fail') {
      e.reply('踩雷，游戏结束')
    }
    // 判断胜利
    if (
      +gamemap[e.channel_id].rows * +gamemap[e.channel_id].cols -
      Object.values(gamemap[e.channel_id].isopen).filter(v => v).length ==
      gamemap[e.channel_id].z
    ) {
      e.reply('恭喜您成功了')
      gameover = true
    }
    generate(e)
    if (gameover) {
      over(e)
    }
  }
}

// 以下为定义方法

/**
 * 初始化游戏
 */
async function newgame(e: AMessage) {
  if (!gamemap[e.channel_id]) {
    gamemap[e.channel_id] = {}
    gamemap[e.channel_id].mines = {} // 标记雷：{1-1：true}
    gamemap[e.channel_id].isopen = {} // 标记翻开：{1-1：true}
    gamemap[e.channel_id].archive = {} // 标记值：{1-1：1}
    gamemap[e.channel_id].game = false // 游戏是否正在运行
    gamemap[e.channel_id].z = 10 // 雷数量
    gamemap[e.channel_id].rows = 9 // 横
    gamemap[e.channel_id].cols = 9 // 竖
    gamemap[e.channel_id].mun = [] // 左上角显示雷数量
    gamemap[e.channel_id].data_map1 = [] // 添加数字后的地图数据
    gamemap[e.channel_id].mark = {} // 标记样式
    gamemap[e.channel_id].player = e.user_id // 游戏发起人
    gamemap[e.channel_id].command = '' // 当前指令
  }
}

async function goo(e: AMessage) {
  /**
   * 开始游戏
   * 生成随机地图
   * 生成各类坐标
   *
   * 输出
   * 标记雷：{1-1：true}
   * 标记翻开：{1-1：true}
   * 标记样式：{1-1：true}
   * 标记值：{1-1：1}
   *
   * 输出无误设置游戏状态
   * game = true
   */

  if (gamemap[e.channel_id].game) {
    e.reply('排雷游戏已经开始')
    generate(e)
    return
  }

  const data_map = [] // 雷数据

  // 生成x*y个字符，值为'-'，表示未翻开
  const numbers = Array.from(
    { length: +gamemap[e.channel_id].rows * +gamemap[e.channel_id].cols },
    () => '0'
  )

  // 随机替换z个数字为9，表示雷
  const indexes = []
  while (indexes.length < gamemap[e.channel_id].z) {
    const index = Math.floor(
      Math.random() * +gamemap[e.channel_id].rows * +gamemap[e.channel_id].cols
    )
    if (!indexes.includes(index)) {
      indexes.push(index)
      numbers[index] = '9'
    }
  }
  // 遍历numbers，每rows个数换一行
  for (let i = 0; i < numbers.length; i += +gamemap[e.channel_id].rows) {
    data_map.push(numbers.slice(i, i + +gamemap[e.channel_id].rows))
  }

  /**
   * 添加坐标，标记雷
   * 设置玩家看到的样式，是否翻开
   * 设置标记
   * 设置显示的坐标
   */
  for (let i = 0; i < +gamemap[e.channel_id].cols; i++) {
    for (let j = 0; j < +gamemap[e.channel_id].rows; j++) {
      // 标记雷
      if (data_map[i][j] == 9) {
        gamemap[e.channel_id].mines[i + '-' + j] = true
      } else {
        gamemap[e.channel_id].mines[i + '-' + j] = false
      }

      // 设置样式是否翻开
      gamemap[e.channel_id].isopen[i + '-' + j] = false

      // 设置标记
      gamemap[e.channel_id].mark[i + '-' + j] = false

      // 设置显示的坐标
      gamemap[e.channel_id].mun.push(ABC[i] + j)
    }
  }
  // 实际雷数量
  // console.log(Object.values(gamemap[e.channel_id].mines).filter(v => v).length)

  for (let i = 0; i < gamemap[e.channel_id].cols; i++) {
    for (let j = 0; j < gamemap[e.channel_id].rows; j++) {
      const isMines = gamemap[e.channel_id].mines[i + '-' + j]
      const number = Object.values({
        0: gamemap[e.channel_id].mines[`${i - 1}-${j - 1}`],
        1: gamemap[e.channel_id].mines[`${i - 1}-${j}`],
        2: gamemap[e.channel_id].mines[`${i - 1}-${j + 1}`],
        3: gamemap[e.channel_id].mines[`${i}-${j - 1}`],
        4: gamemap[e.channel_id].mines[`${i}-${j + 1}`],
        5: gamemap[e.channel_id].mines[`${i + 1}-${j - 1}`],
        6: gamemap[e.channel_id].mines[`${i + 1}-${j}`],
        7: gamemap[e.channel_id].mines[`${i + 1}-${j + 1}`]
      }).filter(v => v).length
      if (isMines) {
        gamemap[e.channel_id].archive[i + '-' + j] = 9
        gamemap[e.channel_id].data_map1.push(9)
      } else {
        gamemap[e.channel_id].archive[i + '-' + j] = number ? number : '0'
        gamemap[e.channel_id].data_map1.push(number ? number : '-')
      }
    }
  }
  // 上帝地图
  // console.log(gamemap[e.channel_id].archive);
  gamemap[e.channel_id].mun = String(gamemap[e.channel_id].mun)
  generate(e)
  gamemap[e.channel_id].game = true
}

/**
 * 重置游戏
 */
async function over(e: AMessage) {
  gamemap[e.channel_id].mines = {} // 标记雷：{1-1：true}
  gamemap[e.channel_id].isopen = {} // 标记翻开：{1-1：true}
  gamemap[e.channel_id].archive = {} // 标记值：{1-1：1}
  gamemap[e.channel_id].game = false // 游戏是否正在运行
  gamemap[e.channel_id].mun = [] // 左上角显示雷数量
  gamemap[e.channel_id].data_map1 = [] // 添加数字后的地图数据
}

/**
 * 发送图片
 */
async function generate(e: AMessage) {
  // console.log('地图' + data_map1)
  // console.log('雷' + mun)
  // console.log('值' , archive)
  // console.log('标记' ,isopen)

  /**
   * 0：翻开空
   * 1-8：雷数量
   * 9：雷
   * -：未翻开
   * +：标记
   * x：踩中的雷
   */

  const data_map = [] // 输出的地图
  const val = Object.values(gamemap[e.channel_id].archive)
  // console.log(val)
  let a = +gamemap[e.channel_id].z // 输出的地雷数量

  const mark1 = Object.values(gamemap[e.channel_id].mark) // 标记的坐标
  // console.log(gamemap[e.channel_id].archive);
  // console.log(gamemap[e.channel_id].mark);
  // console.log(gamemap[e.channel_id].isopen);

  const found = val.includes('x')
  if (found) {
    // 遍历翻开
    val.forEach((el, index) => {
      // 显示翻开
      data_map.push(val[index])
    })
  } else {
    // 遍历翻开
    Object.values(gamemap[e.channel_id].isopen).forEach((el, index) => {
      if (el) {
        // 显示翻开
        data_map.push(val[index])
      } else if (mark1[index]) {
        a = a - 1
        data_map.push('+')
      } else {
        data_map.push('-')
      }
    })
  }

  // 创建地图数组
  const map = String(data_map)

  /**
   * map 地图
   * headurl 右上角头像
   * l 左上角雷数量
   * x 宽度
   * c 画布尺寸
   * muns 地图中显示的数字
   * command 当前指令
   */
  const data = {
    map: map,
    headurl: e.user_avatar,
    l: a,
    x: gamemap[e.channel_id].rows,
    c: 768,
    muns: gamemap[e.channel_id].mun,
    command: gamemap[e.channel_id].command,
    _res: cwd
  }

  const img = await oImages('/resources/html/扫雷.html', data)
  if (img) e.reply(img)
  return false
}
